<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        // js的数据类型：
        // 基本数据类型和引用数据类型
        // 基本数据类型： number string boolean null undefined symbol bigint
        // 引用数据类型： object(对象)
        // 对象： 数组、对象、函数 ....

        // 基本数据类型和引用数据类型是如何存储的？
        // 基本数据类型： 值类型，存储在栈中
        // 引用数据类型： 引用类型，存储在堆中

        var a = 1;
        var b = a;
        b = 2
        console.log(a);

        var c = {
            name: 'why',
            age: 18
        }
        var d = c;
        d.name = 'zs'
        console.log(c.name);

        const { log } = console
        // 判断数据类型的方法：
        // 1. typeof 判断基本数据类型和函数 typeof 函数对象  ===> 'function'
        var fn = {}
        // 判断fn是否是一个函数
        if (typeof fn === 'function') {
            log('fn是一个函数')
        } else {
            log('fn不是一个函数')
        }
        // 2. instanceof 判断对象类型: 判断对象是否是一个构造函数的实例
        var arr = []

        if (arr instanceof Array) {
            log('arr是一个数组')
        } else {
            log('arr不是一个数组')
        }
        var promise = Promise.resolve()
        if (promise instanceof Promise) {
            log('promise是一个Promise对象')
        } else {
            log('promise不是一个Promise对象')
        }

        // 3. Array.isArray 用来判断数组

        if (Array.isArray(arr)) {
            log('arr是一个数组')
        } else {
            log('arr不是一个数组')
        }

        // 4. Object.prototype.toString.call(obj) 用来判断对象类型 可以判断任何类型
        // toString()方法 是用来返回一个对象的字符串描述信息 格式：[object 类型]
        if (Object.prototype.toString.call(arr) == '[object Array]') {
            log('arr是一个数组')
        }

        // 5. constructor 用来判断对象类型

        if (arr.constructor == Array) {
            log('arr是一个数组')
        }


        // 1. 创建对象的方式
        // 1. 对象字面量  var 变量名 = {}  var 变量名 = 'abc' 
        // 弊端： 都是Object的类型的对象 在创建大量对象的情况下不利于代码的复用
        var obj = {}
        // var wangmingyang = {
        //     name: '王明洋',
        //     age: 18,
        //     say: function () {
        //         log('我叫' + this.name + '今年' + this.age + '岁')
        //     }
        // }
        // 2. 使用new关键字 new 构造函数  var 变量名 = new 构造函数()
        // 特点：明确对象的类型 弊端：如果每个对象中都存在相同的操作方法的话 容易浪费内存空间
        var obj = new Object()
        function Student(name, age) {
            this.name = name
            this.age = age
            // 浪费内存空间 相同逻辑的函数被重复创建 并且保存到了不同的内存空间中
            // this.say = function () {
            //     log('我叫' + this.name + '今年' + this.age + '岁')
            // }
        }
        // 优化 构造函数+原型的方式 把实例对象所公有的函数放在原型对象上面
        Student.prototype.say = function () {
            log('我叫' + this.name + '今年' + this.age + '岁')
        }

        // 3. 工厂函数的方式 弊端：不能够明确创建对象的类型
        // 特点： 提高代码的复用性
        function getStudent(name, age) {
            return {
                name,
                age,
                say: function () {
                    log('我叫' + this.name + '今年' + this.age + '岁')
                }
            }
        }
        var wangmingyang = new Student('王明洋', 18)
        var zhangsan = new Student('张三', 18)
        var lisi = new Student('lisi', 18)
        log(wangmingyang.say === zhangsan.say) // false
        log(wangmingyang, zhangsan, lisi)


        // 4. 使用ES6 class的形式
        // 特点：ES6新增的类语法
        class StudentClass {
            constructor(name, age) {
                this.name = name
                this.age = age
            }
            // 自动的放在实例对象的原型上面
            say() {
                log('我叫' + this.name + '今年' + this.age + '岁')
            }
        }

        // 5. 使用一些js内置的方法创建对象Object.create(创建对象的原型)
        var obj = Object.create({ name: 'father' })
        log(obj)



        // 创建基本数据类型的对象
        var str = '' //使用字面量的形式创建字符串
        // var str = new String('') //String是基本数据类型的包装类型

        str.toUpperCase()


    </script>
</body>

</html>